package org.bozo.im.service;

import java.util.ArrayList;
import java.util.Hashtable;

import org.bozo.im.common.MessageContent;
import org.bozo.im.common.MessageType;
import org.bozo.im.common.types.AccountDesc;
import org.bozo.im.common.types.ConnectionID;
import org.bozo.im.common.types.Protocol;
import org.bozo.im.service.core.database.DatabaseHelper;
import org.bozo.im.service.core.roster.IMRoster;
import org.bozo.im.service.protocols.facebook.FacebookSessionBuilder;
import org.bozo.im.service.protocols.fake.FakeSessionBuilder;
import org.bozo.im.service.protocols.gtalk.GTalkSessionBuilder;
import org.bozo.im.service.protocols.jabber.JabberSessionBuilder;
import org.bozo.im.service.protocols.xmpp.XMPPSessionBuilder;

import android.app.Service;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

public class IMService extends Service {

	private static IMService mInstance;
	
	public IMService() {
		mInstance = this;
	}
	
	public static IMService getInstance() {
		if (mInstance == null)
			mInstance = new IMService();
		return mInstance;
	}
	
	private static Hashtable<Protocol, IMSessionBuilder> mBuilders;
	private static ArrayList<Protocol> mProtocols;

	static {
		mBuilders = new Hashtable<Protocol, IMSessionBuilder>();
		mBuilders.put(Protocol.XMPP, new JabberSessionBuilder());
		mBuilders.put(Protocol.FAKE, new FakeSessionBuilder());
		mBuilders.put(Protocol.GTALK, new GTalkSessionBuilder());
		mBuilders.put(Protocol.FACEBOOK, new FacebookSessionBuilder());

		mProtocols = new ArrayList<Protocol>();
		mProtocols.add(Protocol.FAKE);
		mProtocols.add(Protocol.FACEBOOK);
		mProtocols.add(Protocol.GTALK);
	}

	private ArrayList<Messenger> mClients;
	private Messenger mMessenger;
	
	private DatabaseHelper mDBHelper;

	private IMRoster mRoster;
	private Hashtable<ConnectionID, IMSession> mSessions;

	@Override
	public void onCreate() {
		super.onCreate();
		mDBHelper = new DatabaseHelper(this);
		mClients = new ArrayList<Messenger>();
		mMessenger = new Messenger(new IMServiceHandler(this));
		mRoster = new IMRoster(this, mDBHelper);
		mSessions = new Hashtable<ConnectionID, IMSession>();
	}
	
	@Override
	public IBinder onBind(Intent pIntent) {
		return mMessenger.getBinder();
	}

	@Override
	public void onDestroy() {

		// ---------------------------------------------------------------------------------------
		Log.d(Constants.ACTIVITY_TAG, "org.bozo.im.service.IMService.onDestroy ---------- Destroying service");
		// ---------------------------------------------------------------------------------------

		super.onDestroy();
	}

	public IMRoster getRoster() {
		return mRoster;
	}

	public DatabaseHelper getDatabaseHelper() {
		return mDBHelper;
	}
	
	public void updateRoster() {

		// ---------------------------------------------------------------------------------------
		Log.d(Constants.ACTIVITY_TAG, "org.bozo.im.service.IMService.updateContactList ---------- Sending update notification to Activity");
		// ---------------------------------------------------------------------------------------

		Bundle lData = new Bundle();
		lData.putParcelableArrayList(MessageContent.CONTACTS, getRoster().getContactsList());

		Message lMessage = Message.obtain();
		lMessage.what = MessageType.GET_ROSTER_RESPONSE;
		lMessage.replyTo = mMessenger;
		lMessage.setData(lData);

		sendMessage(lMessage);
	}

	private class IMServiceHandler extends Handler {

		public IMService mService;

		public IMServiceHandler(IMService pService) {
			mService = pService;
		}

		@Override
		public void handleMessage(Message pMsg) {

			Message lMessage;
			Bundle lBundle;

			String lLogin;
			String lPassword;
			String lServer;
			Protocol lProtocol;
			int lPort;
			SQLiteDatabase lDatabase;

			pMsg.getData().setClassLoader(getClass().getClassLoader());

			switch (pMsg.what) {

			case MessageType.REGISTER_CLIENT:

				// ---------------------------------------------------------------------------------------
				Log.d(Constants.ACTIVITY_TAG, "org.bozo.im.service.IMService.IMServiceHandler.handleMessage ---------- Activity registered");
				// ---------------------------------------------------------------------------------------

				mClients.add(pMsg.replyTo);
				break;

			case MessageType.UNREGISTER_CLIENT:

				// ---------------------------------------------------------------------------------------
				Log.d(Constants.ACTIVITY_TAG, "org.bozo.im.service.IMService.IMServiceHandler.handleMessage ---------- Activity unregistered");
				// ---------------------------------------------------------------------------------------

				mClients.remove(pMsg.replyTo);
				break;

			case MessageType.ADD_ACCOUNT:

				AccountDesc lAccount = (AccountDesc)pMsg.getData().getParcelable(MessageContent.ACCOUNT);

				lProtocol = lAccount.getProtocol();
				lLogin = lAccount.getLoginName();
				lPassword = lAccount.getPassword();
				lServer = lAccount.getServer();
				lPort = lAccount.getPort();
				
				ContentValues lValues = new ContentValues();
				lValues.put(DatabaseHelper.ACCOUNT_FIELD_PROTOCOL, lProtocol.name());
				lValues.put(DatabaseHelper.ACCOUNT_FIELD_LOGIN, lLogin);
				lValues.put(DatabaseHelper.ACCOUNT_FIELD_PASSWORD, lPassword);
				lValues.put(DatabaseHelper.ACCOUNT_FIELD_SERVER, lServer);
				lValues.put(DatabaseHelper.ACCOUNT_FIELD_PORT, lPort);

				lDatabase = getDatabaseHelper().getWritableDatabase();
				
				lDatabase.insert(DatabaseHelper.ACCOUNT_TABLE_NAME, null, lValues);
				
				login(lProtocol, lLogin, lPassword, lServer, lPort);

				break;
				
			case MessageType.GET_ROSTER:

				// ---------------------------------------------------------------------------------------
				Log.d(Constants.ACTIVITY_TAG, "org.bozo.im.service.IMService.IMServiceHandler.handleMessage ---------- Started sessions count: " + mSessions.size());
				// ---------------------------------------------------------------------------------------

				if (mSessions.isEmpty()) {

					lDatabase = getDatabaseHelper().getReadableDatabase();
					
					Cursor lCursor = lDatabase.query(DatabaseHelper.ACCOUNT_TABLE_NAME, null, null, null, null, null, null);
					
					if (lCursor.moveToFirst()) {

						int lLoginIndex = lCursor.getColumnIndex(DatabaseHelper.ACCOUNT_FIELD_LOGIN);
						int lPasswordIndex = lCursor.getColumnIndex(DatabaseHelper.ACCOUNT_FIELD_PASSWORD);
						int lPortIndex = lCursor.getColumnIndex(DatabaseHelper.ACCOUNT_FIELD_PORT);
						int lServerIndex = lCursor.getColumnIndex(DatabaseHelper.ACCOUNT_FIELD_SERVER);
						int lProtocolIndex = lCursor.getColumnIndex(DatabaseHelper.ACCOUNT_FIELD_PROTOCOL);

						do {
							lLogin = lCursor.getString(lLoginIndex);
							lPassword = lCursor.getString(lPasswordIndex);
							lServer = lCursor.getString(lServerIndex);
							lProtocol = Protocol.valueOf(lCursor.getString(lProtocolIndex));
							lPort = lCursor.getInt(lPortIndex);

							login(lProtocol, lLogin, lPassword, lServer, lPort);

						} while (lCursor.moveToNext());
					}

				} else {
					updateRoster();
				}

				break;

			case MessageType.GET_PROTOCOLS:

				lMessage = Message.obtain();
				lBundle = new Bundle();
				lBundle.putParcelableArrayList(MessageContent.PROTOCOLS, mProtocols);

				lMessage.what = MessageType.GET_PROTOCOLS_RESPONSE;
				lMessage.replyTo = mMessenger;
				lMessage.setData(lBundle);

				mService.sendMessage(lMessage, pMsg.replyTo);
				break;

			case MessageType.GET_PROTOCOL_REQUIREMENT:
				lProtocol = (Protocol)pMsg.getData().getParcelable(MessageContent.PROTOCOL);

				lMessage = Message.obtain();
				lBundle = new Bundle();
				lBundle.putParcelableArrayList(MessageContent.PROTOCOL_REQUIREMENTS, mBuilders.get(lProtocol).getRequirements());

				lMessage.what = MessageType.GET_PROTOCOL_REQUIREMENT_RESPONSE;
				lMessage.replyTo = mMessenger;
				lMessage.setData(lBundle);

				mService.sendMessage(lMessage, pMsg.replyTo);
				break;

			default:
				super.handleMessage(pMsg);
			}

		}
	}
	
	private void login(Protocol pProtocol, String pLogin, String pPassword, String pServer, int pPort) {

		IMSessionBuilder lBuilder = mBuilders.get(pProtocol);
		IMSessionConfiguration lConfiguration = lBuilder.buildConfiguration(pLogin, pPassword, pServer, pPort);
		IMSession lSession = lBuilder.buildSession(lConfiguration);

		// ---------------------------------------------------------------------------------------
		Log.d(Constants.ACTIVITY_TAG, "org.bozo.im.service.IMService.login ---------- ");
		// ---------------------------------------------------------------------------------------
		
		ConnectionID lId = new ConnectionID(pProtocol, pLogin);

		if (!mSessions.containsKey(lId)) {
			mSessions.put(lId, lSession);
			lSession.login();
		}
	}

	private void sendMessage(Message pMessage) {
		for (Messenger lClient : mClients) {
			sendMessage(pMessage, lClient);
		}
	}

	private void sendMessage(Message pMessage, Messenger pMessenger) {
		try {
			pMessenger.send(pMessage);
		} catch (RemoteException e) {
			// ---------------------------------------------------------------------------------------
			Log.e(Constants.ACTIVITY_TAG, "org.bozo.im.service.IMService.sendMessage ---------- RemoteException: ", e);
			// ---------------------------------------------------------------------------------------
		}
	}
}
